/******************************************************************************\
    Object.hpp contains classes for all self-destructing objects.
    Copyright (C) 2008  Victor Vinogradov (fly3333@gmail.com)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
\******************************************************************************/

#ifndef __OBJECT_HPP
#define __OBJECT_HPP

//#include <windows.h>
#include <crtdbg.h>

/* Define NULL pointer value */
#ifndef NULL
#ifdef __cplusplus
#define NULL    0
#else
#define NULL    ((void *)0)
#endif
#endif

class cSubObject
{
	int UsersCount;
public:
	cSubObject()
		: UsersCount( 1 )
	{
	}
	virtual ~cSubObject()
	{
	}
	void Attach()
	{
		++UsersCount;
	}
	void Detach()
	{
		if( --UsersCount == 0 )
			delete this;
	}
};

template< class cWardObject >
class cSubPointerObject : public cSubObject
{
	cWardObject *WardObject;
public:
	cSubPointerObject( cWardObject *ward_object )
		: WardObject( ward_object )
	{
	}
	~cSubPointerObject()
	{
		delete WardObject;
	}
	cWardObject *Pointer() const
	{
		return WardObject;
	}
};

template< class cSubObjectType >
class cObject
{
private:
	cSubObjectType *SubObject;
protected:
	void SwitchToSubObject( cSubObjectType *sub_object )
	{
		if( SubObject != NULL )
			SubObject->Detach();
		SubObject = sub_object;
	}
	cSubObjectType *GetSubObject() const
	{
		return SubObject;
	}
public:
	cObject()
		: SubObject( NULL )
	{
	}
	cObject( const cObject &object )
		: SubObject( object.SubObject )
	{
		if( SubObject != NULL )
			SubObject->Attach();
	}
	virtual ~cObject()
	{
		if( SubObject != NULL )
			SubObject->Detach();
	}
	cObject &operator =( const cObject &object )
	{
		if( SubObject != NULL )
			SubObject->Detach();
		SubObject = object.SubObject;
		if( SubObject != NULL )
			SubObject->Attach();
		return *this;
	}
};

template< class cWardObjectType >
class cPointerObject : public cObject< cSubPointerObject< cWardObjectType > >
{
protected:
	bool SwitchToWardObject( cWardObjectType *ward_object )
	{
		cSubPointerObject< cWardObjectType > *sub_object;
		try
		{
			sub_object = new cSubPointerObject< cWardObjectType >( ward_object );
		}
		catch( ... )
		{
			return false;
		}
		SwitchToSubObject( sub_object );
		return true;
	}
public:
	cWardObjectType &operator *() const
	{
		_ASSERT( GetSubObject() != NULL );
		return *GetSubObject()->Pointer();
	}
	cWardObjectType *operator ->() const
	{
		_ASSERT( GetSubObject() != NULL );
		return GetSubObject()->Pointer();
	}
};

#endif
